'''
一重、二重、三重和CBC
'''
import random

W0 = ""
W1 = ""
W2 = ""
W3 = ""
W4 = ""
W5 = ""
S1_box = [["1001", "0100", "1010", "1011"], ["1101", "0001", "1000", "0101"],
          ["0110", "0010", "0000", "0011"], ["1100", "1110", "1111", "0111"]]  # S盒
S2_box = [["1010", "0101", "1001", "1011"], ["0001", "0111", "1000", "1111"],
          ["0110", "0000", "0010", "0011"], ["1100", "0100", "1101", "1110"]]  # 逆S盒


def get_Key(k):
    '''
    输入主密匙，获取K1,K2
    '''
    main_Key = k
    global W0
    global W1
    global W2
    global W3
    global W4
    global W5
    W0 = main_Key[0:8]
    W1 = main_Key[8:16]
    W2 = xor(W0, g(W1, 1))
    W3 = xor(W1, W2)
    W4 = xor(W2, g(W3, 2))
    W5 = xor(W4, W3)


def encrypt(p):
    '''
    加密函数
    '''
    Plainttext = p  # 明文
    # 第一轮
    Plainttext = xor(Plainttext, W0 + W1)  # 轮秘钥加
    Plainttext = replace(Plainttext, 1)  # 半字节替代
    Plainttext = move_hang(Plainttext)  # 行移位
    Plainttext = multiply_lie(Plainttext, 1)  # 列混淆
    # 第二轮
    Plainttext = xor(Plainttext, W2 + W3)  # 轮秘钥加
    Plainttext = replace(Plainttext, 1)  # 半字节替代
    Plainttext = move_hang(Plainttext)  # 行移位
    ciphertext = xor(Plainttext, W4 + W5)  # 轮秘钥加
    print("密文为: " + ciphertext)
    return ciphertext


def decrypt(c):
    '''
    解密函数
    '''
    ciphertext = c  # 秘文
    # 轮秘钥加
    ciphertext = xor(ciphertext, W4 + W5)
    # 行移位
    ciphertext = move_hang(ciphertext)
    # 半字节替代
    ciphertext = replace(ciphertext, 2)
    # 轮秘钥加
    ciphertext = xor(ciphertext, W2 + W3)
    # 列混淆
    ciphertext = multiply_lie(ciphertext, 2)
    # 行移位
    ciphertext = move_hang(ciphertext)
    # 半字节替代
    ciphertext = replace(ciphertext, 2)
    Plainttext = xor(ciphertext, W0 + W1)  # 轮秘钥加

    print("明文为: " + Plainttext)
    return Plainttext


def g(key, n):
    '''
    根据key生成轮密钥
    n代表的是第几轮
    '''
    N0 = key[0:4]
    N1 = key[4:8]
    N1_ = searchSbox(N1, 1)
    N0_ = searchSbox(N0, 1)
    N_ = N1_ + N0_
    W_ = ""
    if n == 1:
        W_ = xor(N_, "10000000")
    elif n == 2:
        W_ = xor(N_, "00110000")
    return W_


def searchSbox(str, n):
    '''
    用于在 S1_box 或 S2_box 中查找对应的值
    输入:str,n
    它接收一个二进制字符串  str  和一个整数  n  作为参数，根据  n  的值选择要查找的 S_box，并返回对应的值。
    '''
    ret = str[0] + str[1]
    ret1 = str[2] + str[3]
    retu = ""
    if n == 1:
        retu = S1_box[int(ret, 2)][int(ret1, 2)]
    else:
        retu = S2_box[int(ret, 2)][int(ret1, 2)]
    return retu


def xor(str, key):
    '''
    异或运算
    '''
    sb = []
    for i in range(len(str)):
        if str[i] == key[i]:
            sb.append("0")
        else:
            sb.append("1")
    return "".join(sb)


def replace(str, n):
    '''
    半字节替换
    n代表半字节替换或是逆替换
    '''
    str0 = str[0:4]
    str1 = str[4:8]
    str2 = str[8:12]
    str3 = str[12:16]
    str0 = searchSbox(str0, n)
    str1 = searchSbox(str1, n)
    str2 = searchSbox(str2, n)
    str3 = searchSbox(str3, n)
    return str0 + str1 + str2 + str3


def move_hang(str):
    '''
    行移位
    '''
    str0 = str[0:4]
    str1 = str[4:8]
    str2 = str[8:12]
    str3 = str[12:16]
    str_ = str1
    str1 = str3
    str3 = str_
    return str0 + str1 + str2 + str3


def multiply_lie(str, n):
    '''
    列混淆
    '''
    s0 = str[0:4]
    s1 = str[4:8]
    s2 = str[8:12]
    s3 = str[12:16]
    s0_ = "0000"
    s1_ = "0000"
    s2_ = "0000"
    s3_ = "0000"
    if n == 1:
        s1_4 = multiply(s1, 4)
        s0_ = xor(s0, multiply(s1, 4))
        s1_ = xor(s1, multiply(s0, 4))
        s2_ = xor(s2, multiply(s3, 4))
        s3_ = xor(s3, multiply(s2, 4))
    elif n == 2:
        s0_ = xor(multiply(s0, 9), multiply(s1, 2))
        s1_ = xor(multiply(s0, 2), multiply(s1, 9))
        s2_ = xor(multiply(s2, 9), multiply(s3, 2))
        s3_ = xor(multiply(s2, 2), multiply(s3, 9))
    return s0_ + s1_ + s2_ + s3_


def multiply(str, n):
    '''
    乘法
    '''
    result = ""
    if n == 2:
        if str == "0000":
            result = "0000"
        elif str == "0001":
            result = "0010"
        elif str == "0010":
            result = "0100"
        elif str == "0011":
            result = "0110"
        elif str == "0100":
            result = "1000"
        elif str == "0101":
            result = "1010"
        elif str == "0110":
            result = "1100"
        elif str == "0111":
            result = "1110"
        elif str == "1000":
            result = "0011"
        elif str == "1001":
            result = "0001"
        elif str == "1010":
            result = "0111"
        elif str == "1011":
            result = "0101"
        elif str == "1100":
            result = "1011"
        elif str == "1101":
            result = "1001"
        elif str == "1110":
            result = "1111"
        elif str == "1111":
            result = "1101"

    if n == 4:
        if str == "0000":
            result = "0000"
        elif str == "0001":
            result = "0100"
        elif str == "0010":
            result = "1000"
        elif str == "0011":
            result = "1100"
        elif str == "0100":
            result = "0011"
        elif str == "0101":
            result = "0111"
        elif str == "0110":
            result = "1011"
        elif str == "0111":
            result = "1111"
        elif str == "1000":
            result = "0110"
        elif str == "1001":
            result = "0010"
        elif str == "1010":
            result = "1110"
        elif str == "1011":
            result = "1010"
        elif str == "1100":
            result = "0101"
        elif str == "1101":
            result = "0001"
        elif str == "1110":
            result = "1101"
        elif str == "1111":
            result = "1001"

    if n == 9:
        if str == "0000":
            result = "0000"
        elif str == "0001":
            result = "1001"
        elif str == "0010":
            result = "0001"
        elif str == "0011":
            result = "1000"
        elif str == "0100":
            result = "0010"
        elif str == "0101":
            result = "1011"
        elif str == "0110":
            result = "0011"
        elif str == "0111":
            result = "1010"
        elif str == "1000":
            result = "0100"
        elif str == "1001":
            result = "1101"
        elif str == "1010":
            result = "0101"
        elif str == "1011":
            result = "1100"
        elif str == "1100":
            result = "0110"
        elif str == "1101":
            result = "1111"
        elif str == "1110":
            result = "0111"
        elif str == "1111":
            result = "1110"

    return result


def yien(k, p):  # 一重加密
    print("!!!")
    get_Key(k)
    r = encrypt(p)
    return r


def yide(k, c):  # 一重解密
    print("!!!")
    get_Key(k)
    r = decrypt(c)
    return r


def shuangen(k, p):  # 双重加密
    print("!!!")
    k1 = k[0:16]
    k2 = k[16:32]
    get_Key(k1)
    r1 = encrypt(p)
    get_Key(k2)
    print("shuangchong")
    r2 = encrypt(r1)
    return r2


def shuangde(k, c):  # 双重解密
    print("!!!")
    k1 = k[0:16]
    k2 = k[16:32]
    get_Key(k2)
    r1 = decrypt(c)
    get_Key(k1)
    print("shuangchong")
    r2 = decrypt(r1)
    return r2


def sanen(k, p):  # 三重加密
    print("!!!")
    k1 = k[0:16]
    k2 = k[16:32]
    k3 = k[32:48]
    get_Key(k1)
    r1 = encrypt(p)
    get_Key(k2)
    r2 = encrypt(r1)
    get_Key(k3)
    print("sancong")
    r3 = encrypt(r2)
    return r3


def sande(k, c):  # 三重解密
    print("!!!")
    k1 = k[0:16]
    k2 = k[16:32]
    k3 = k[32:48]
    get_Key(k3)
    r1 = decrypt(c)
    get_Key(k2)
    r2 = decrypt(r1)
    get_Key(k1)
    print("sancong")
    r3 = decrypt(r2)
    return r3


def CBC_en(text, k):  # CBC加密
    IV = '1010101010101010'
    C = '0'
    for i in range(int(len(text) / 16)):
        p0 = text[i * 16:(i + 1) * 16]  # 更新当前分组的明文p0
        p1 = xor(p0, IV)  # 与IV进行异或操作，使用函数xor，获得p1
        c = yien(k, p1)  # k为密钥，p1为明文进行S-AES加密
        IV = c
        C = C + c  # 连接密文
    Result = C[1:len(text) + 1]
    print("CBC模式下的S-AES加密结果：", Result)
    return Result


def CBC_de(text, k):  # CBC解密
    IV = '1010101010101010'
    P = '0'
    for i in range(int(len(text) / 16)):
        c0 = text[i * 16:(i + 1) * 16]  # 更新当前分组的密文c0
        p = yide(k, c0)  # k为密钥，c0为密文进行S-AES解密
        p1 = xor(p, IV)  # 与IV进行异或操作，使用函数xor，获得p1
        IV = c0
        P = P + p1  # 连接密文
    Result = P[1:len(text) + 1]
    print("CBC模式下的S-AES解密结果：", Result)
    return Result


def CBC(text, k):  # CBC模式
    return CBC_en(text, k), CBC_de(CBC_en(text, k), k)


if __name__ == "__main__":
    # yien('1000110111101111', '1000110100101011')
    # yide('1000110111101111', '1101010001100011')
    # shuangen('10101111101010101010111110101010', '1111111011101111')
    # shuangde('10101111101010101010111110101010', '1011001111011010')
    # sanen('101100111101101010110011110110101011001111011010', '1111111011101111')
    # sande('101100111101101010110011110110101011001111011010', '0000111111101011')
    CBC('111100001111000011110000111100001111000011110000', '1111111011101111')

    # CBC对比篡改密文前后的解密结果
    k = '1111111011101111'
    p_CBC = '110011001100110011001100110011001100110011001100'
    print("明文为：", p_CBC, "\n密钥为：", k)
    m = CBC_en(p_CBC, k)
    print("对密文进行解密")
    m0 = CBC_de(m, k)
    m1 = '110110110010010111011011001001011110000101011001'
    print("\n更改/替换密文中的片段，将第一个密文分组替换为与第二个密文分组相同，得到错误的密文：", m1)
    print("对错误密文进行解密")
    m2 = CBC_de(m1, k)
    print("\n对两次解密结果进行异或操作，得出错误密文解密出的明文共有19位错误，错误位置见下方，1代表错误：")
    print(xor(m0, m2))
